package http

import (
	"context"
	"errors"
	"log"
	"net"
	"net/http"
	"net/url"
	"sync"
	"time"

	transport2 "voiddme.io/engineering/pkg/voiddme/transport"

	"github.com/gin-gonic/gin"
)

var _ transport2.Server = (*Server)(nil)
var _ transport2.Endpointer = (*Server)(nil)

type ServerOption func(server *Server)

type Server struct {
	*http.Server
	ctx      context.Context
	lis      net.Listener
	once     sync.Once
	endpoint *url.URL
	err      error
	network  string
	address  string
	timeout  time.Duration
	router   *gin.Engine
	log      *log.Logger
}

func NewServer(opts ...ServerOption) *Server {
	srv := &Server{
		network: "tcp",
		address: ":8080",
		timeout: 1 * time.Second,
		log:     log.Default(),
	}
	for _, o := range opts {
		o(srv)
	}
	srv.Server = &http.Server{Handler: srv}
	srv.router = gin.New()
	srv.router.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	return srv
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.router.ServeHTTP(w, r)
}

func (s *Server) Endpoint() (*url.URL, error) {
	s.once.Do(func() {
		lis, err := net.Listen(s.network, s.address)
		if err != nil {
			s.err = err
			return
		}
		s.lis = lis
		s.endpoint = &url.URL{
			Scheme: "http",
			Host:   s.address,
		}
	})
	if s.err != nil {
		return nil, s.err
	}
	return s.endpoint, nil
}

func (s *Server) Start(ctx context.Context) error {
	if _, err := s.Endpoint(); err != nil {
		return err
	}
	s.ctx = ctx
	if err := s.Serve(s.lis); !errors.Is(err, http.ErrServerClosed) {
		return err
	}
	return nil
}

func (s *Server) Stop(ctx context.Context) error {
	return s.Shutdown(context.Background())
}
